local lvshi = require 'packages/lvshisha/utility/_base'
local U = require "packages/utility/utility"
local DIY = require "packages/diy_utility/diy_utility"

--策略相关

--获取角色的当前策略
---@param player Player @ 角色
---@param wonder_map Boolean @ 是否考虑天机图
lvshi.getStrategy = function(player, wonder_map)
  wonder_map = (wonder_map == nil) and true or wonder_map
  if player:hasSkill("#lvshi__wonder_map_skill") and wonder_map then
    return "schemecourage"
  end
  if player:getMark("@!scheme") > 0 then
    return "scheme"
  elseif player:getMark("@!courage") > 0 then
    return "courage"
  elseif player:getMark("@!schemecourage") > 0 then
    return "schemecourage"
  end
  return "none"
end
--获取角色的武将牌上的策略
lvshi.getTrueStrategy = function(player)
  if Fk.generals[player.general].strategy then
    return Fk.generals[player.general].strategy
  else
    return "none"
  end
end
--设置角色的策略
lvshi.setStrategy = function(player, strategy)
  player.room:setPlayerMark(player, "@!scheme", 0)
  player.room:setPlayerMark(player, "@!courage", 0)
  player.room:setPlayerMark(player, "@!schemecourage", 0)
  if strategy ~= "none" then
    player.room:setPlayerMark(player, "@!"..strategy, 1)
  end
end
--重置角色的策略至初始状态
lvshi.resetStrategy = function(player)
  lvshi.setStrategy(player, lvshi.getTrueStrategy(player))
end
--令一名角色失去策略（指定或自选）
lvshi.loseStrategy = function(player, strategy)
  local strategys = {}
  if player:getMark("@!scheme") > 0 then
    table.insertIfNeed(strategys, "scheme")
  elseif player:getMark("@!courage") > 0 then
    table.insertIfNeed(strategys, "courage")
  elseif player:getMark("@!schemecourage") > 0 then
    table.insertTable(strategys, {"scheme","courage"})
  end
  if strategy == "choice" then
    strategy = player.room:askForChoice(player, strategys, "loseStrategy")
  end
  table.removeOne(strategys, strategy)
  player.room:sendLog{type = "#loseStrategy", from = player.id, arg = strategy, toast = true}
  if #strategys == 0 then
    lvshi.setStrategy(player, "none")
  elseif #strategys == 1 then
    lvshi.setStrategy(player, strategys[1])
  else
    lvshi.setStrategy(player, "schemecourage")
  end
end
--令一名角色获得策略
lvshi.gainStrategy = function(player, strategy)
  local strategys = {}
  if player:getMark("@!scheme") > 0 then
    table.insertIfNeed(strategys, "scheme")
  elseif player:getMark("@!courage") > 0 then
    table.insertIfNeed(strategys, "courage")
  elseif player:getMark("@!schemecourage") > 0 then
    table.insertTable(strategys, {"scheme","courage"})
  end
  table.insertIfNeed(strategys, strategy)
  player.room:sendLog{type = "#gainStrategy", from = player.id, arg = strategy, toast = true}
  if #strategys == 0 then
    lvshi.setStrategy(player, "none")
  elseif #strategys == 1 then
    lvshi.setStrategy(player, strategys[1])
  else
    lvshi.setStrategy(player, "schemecourage")
  end
end

local set_strategy = fk.CreateTriggerSkill{
  name = "#set_strategy",
  global = true,
  mute = true,
  refresh_events = {fk.GameStart},
  can_refresh = function(self, event, target, player, data)
    return lvshi.getTrueStrategy(player) ~= "none"
  end,
  on_refresh = function(self, event, target, player, data)
    lvshi.setStrategy(player, lvshi.getTrueStrategy(player))
  end,
}

Fk:addSkill(set_strategy)

lvshi.trueName = function(card)
  if string.find(card.name, "__slash") then
    return "lvshi__slash"
  end
  return card.name
end

lvshi.isshown = function(card)
  return card:getMark("@@ShownCards-inhand") > 0 or card:getMark("@@ShownCards-inhand-round") > 0
  or card:getMark("@@ShownCards-inhand-turn") > 0 or card:getMark("@@ShownCards-inhand-phase") > 0
end

--获得一名角色的暗置手牌，用于展示，明置
lvshi.gethidecards = function(player)
  local cards = table.filter(player:getCardIds("h"), function (id) return not table.contains(DIY.GetShownCards(player), id) end)
  if #cards > 0 then
    return cards
  end
  return {}
end
--数花色
lvshi.countSuits = function(cards)
  local suits = {}
  for _, id in ipairs(cards) do
    if Fk:getCardById(id).suit ~= Card.NoSuit then
      table.insertIfNeed(suits, Fk:getCardById(id).suit)
    end
  end
  return #suits
end

--数类别
---@param subtype? boolean @ 是否为不算子类别，写false为计算子类别，默认不为
lvshi.countTypes = function(cards, subtype)
  local types = {}
  for _, id in ipairs(cards) do
    if subtype or (subtype == nil) then
      table.insertIfNeed(types, Fk:getCardById(id).type)
    else
      table.insertIfNeed(types, Fk:getCardById(id).sub_type)
    end
  end
  return #types
end

--判断队友
lvshi.isFriend = function(player, target)
  if player.room and (not player.role_shown or not target.role_shown) then return false end
  if player.role == "" or target.role == "" then return false end
  return target.role == player.role or (table.contains({"lord", "loyalist"}, target.role) and table.contains({"lord", "loyalist"}, player.role))
end

--- 造成伤害
---@param from ServerPlayer @ 来源角色
---@param target ServerPlayer @ 目标角色
---@param damage number @ 伤害值
---@param reason type @ 伤害属性
---@param reason string @ 原因
---@param card? Card @ 牌对象
---@return boolean @ 是否成功造成伤害
lvshi.damage = function(from, target, reason, damage, damagetype, card)
  target.room:damage{
    from = from,
    to = target,
    damage = damage or 1,
    damageType = damagetype or fk.NormalDamage,
    reason = reason or "",
    card = card,
  }
  return true
end

--- 回复体力
---@param who ServerPlayer @ 角色
---@param num number @ 回复数量
---@param skillName string @ 技能名
---@return boolean @ 是否成功回复体力
lvshi.recover = function(who, num, skillName)
  local room = who.room
  num = math.min(num, who.maxHp - who.hp)
  if num < 1 then return false end
  room:recover{
    who = who,
    num = num,
    skillName = skillName,
  }
  return true
end

--无效牌并播报
lvshi.nullifiedCard = function(data, card, target)
  if data.card.sub_type == Card.SubtypeDelayedTrick or data.card.type == Card.TypeEquip then
    AimGroup:cancelTarget(data, target.id)
  else
    table.insertIfNeed(data.nullifiedTargets, target.id)
  end
  target.room:sendLog{
    type = "#nullifiedCard",
    from = target.id,
    arg = data.card:toLogString(),
    toast = true,
  }
end

lvshi.choicelog = function(target, choice)
  target.room:sendLog{
    type = "#ChoiceLogtoast",
    from = target.id,
    arg = choice,
    toast = true,
  }
end

Fk:loadTranslationTable{
  ["#nullifiedCard"] = "%arg 对 %from 无效",
  ["#ChoiceLogtoast"] = "%from 选择了 %arg",
}

--置入判定区并视为延时锦囊牌
---@param id int @ 置入的牌
---@param target ServerPlayer @ 目标角色
---@param pattern string @ 被视为的延时锦囊牌
---@param reason string @ 技能名
---@return boolean
lvshi.MoveAsDelayedTrick = function(id, target, pattern, reason)
  local card = Fk:cloneCard(pattern)
  card:addSubcard(id)
  target:addVirtualEquip(card)
  target.room:moveCardTo(card, Player.Judge, target, fk.ReasonJustMove, reason)
end

--有牌的区域数
lvshi.getAreasHasCards = function(player)
  local n = 0
  if #player:getCardIds(Player.Hand) > 0 then n = n + 1 end
  if #player:getCardIds(Player.Equip) > 0 then n = n + 1 end
  if #player:getCardIds(Player.Judge) > 0 then n = n + 1 end
  return n
end

-- 同时选择X项
lvshi.AskForChoicesConcurrent = function(targets, choices, n, reason, prompt)
  local req = Request:new(targets, "AskForChoices")
  req.focus_text = reason
  local req_data = {
    choices, choices, {n, n}, false, reason, prompt, false
  }
  for _, p in ipairs(targets) do
    req:setData(p, req_data)
    req:setDefaultReply(p, table.random(choices, n))
  end
  req:ask()
  local result = {}
  for _, p in ipairs(targets) do
    result[p.id] = req:getResult(p)
  end
  return result
end

--影
lvshi.getShade = function (room, n)
  local ids = {}
  for _, id in ipairs(room.void) do
    if n <= 0 then break end
    if Fk:getCardById(id).trueName == "shade" then
      room:setCardMark(Fk:getCardById(id), MarkEnum.DestructIntoDiscard, 1)
      table.insert(ids, id)
      n = n - 1
    end
  end
  while n > 0 do
    local card = room:printCard("lvshi__shade", Card.Spade, 1)
    room:setCardMark(card, MarkEnum.DestructIntoDiscard, 1)
    table.insert(ids, card.id)
    n = n - 1
  end
  return ids
end

--护甲
local lvshishield_skill = fk.CreateTriggerSkill{
  name = "#lvshishield_skill",
  global = true,
  mute = true,
  events = {fk.DamageInflicted, fk.EventPhaseStart},
  can_trigger = function(self, event, target, player, data)
    --if player.room.settings.gameMode == "ls_2v2" or player.room.settings.gameMode == "ls_1v2_mode" then return false end
    if event == fk.EventPhaseStart then
      return target == player and target.phase == Player.Start and #target:getPile("shield") > 0
    else
		  return target == player and #target:getPile("shield") > 0 and data.skillName ~= "fire__slash_skill"
    end
  end,
  on_cost = Util.TrueFunc,
  on_use = function(self, event, target, player, data)
    local room = player.room
    if event == fk.DamageInflicted then
      if #player:getPile("shield") > data.damage then
        local n = data.damage
        local card = room:askForCard(player, n, n, false, self.name, false, ".|.|.|shield|.|.", "#shield-invoke:::"..n, "shield")
        if #card == 0 then card = table.random(player:getPile("shield"), n) end
        room:moveCardTo(card, Card.DiscardPile, nil, fk.ReasonPutIntoDiscardPile, "", "", true)
        room.logic:trigger("fk.ShieldCosted", player, {ids = card, from = data.from or nil})
        return true
      else
        data.damage = data.damage - #player:getPile("shield")
        local ids = player:getPile("shield")
        room:moveCardTo(ids, Card.DiscardPile, nil, fk.ReasonPutIntoDiscardPile, "", "", true)
        room.logic:trigger("fk.ShieldCosted", player, {ids = ids, from = data.from or nil})
      end
    else
      room:moveCardTo(player:getPile("shield"), Card.PlayerHand, player, fk.ReasonJustMove, self.name, "", true)
    end
  end
}

Fk:addSkill(lvshishield_skill)

Fk:loadTranslationTable{
  ["shield"] = "护甲",
  ["@@shield"] = "护甲",
  ["#lvshishield_skill"] = "护甲",
  ["#shield-invoke"] = "护甲：请弃置 %arg 张护甲抵消 %arg 点伤害",
  ["#shield_remove"] = "%from 移除了护甲：%arg",
  ['lvs__shield'] = "当你受到伤害时，若你有护甲，则你弃置等同于此伤害点数的护甲，防止等量伤害；准备阶段，你获得所有护甲",
}

local xushi_skill = fk.CreateActiveSkill{
  name = "lvshi_xushi_skill&",
  prompt = "#lvshi_xushi_skill&",
  anim_type = "special",
  card_num = 3,
  target_num = 0,
  interaction = function(self)
    local skills = {}
    for _, s in ipairs(Self.player_skills) do
      if Self:getMark("@@lvshi_readying:::"..s.name) > 0 and Self:usedSkillTimes(s.name, Player.HistoryTurn) == 0 then
        table.insert(skills, s.name)
      end
    end
    return UI.ComboBox { choices = skills }
  end,
  can_use = function(self, player)
    return table.find(player.player_skills, function(s)
      return player:getMark("@@lvshi_readying:::"..s.name) > 0 and player:usedSkillTimes(s.name, Player.HistoryTurn) == 0
    end)
  end,
  card_filter = function(self, to_select, selected)
    return #selected < 3 and not Self:prohibitDiscard(to_select)
  end,
  on_use = function(self, room, effect)
    local player = room:getPlayerById(effect.from)
    room:throwCard(effect.cards, self.name, player, player)
    if not player.dead then
      room:setPlayerMark(player, "@@lvshi_readying:::"..self.interaction.data, 0)
      local data = Fk.skills[self.interaction.data]
      room.logic:trigger("fk.XushiSkillReady", player, data, false)
    end
  end,
}
local xushi_skill_trigger = fk.CreateTriggerSkill{
  name = "#xushi_skill_trigger",
  priority = 0.001,
  global = true,

  refresh_events = {fk.EventAcquireSkill, fk.EventLoseSkill, fk.SkillEffect, fk.AfterDrawPileShuffle, fk.GamePrepared},
  can_refresh = function (self, event, target, player, data)
    if event == fk.AfterDrawPileShuffle then
      return table.find(player.player_skills, function(s)
        return player:getMark("@@lvshi_readying:::"..s.name) > 0
      end)
    elseif event == fk.GamePrepared then
      -- 注意，全局触发技能添加到游戏比角色技能晚，无法检测开局添加的角色技能
      return not player:hasSkill("lvshi_xushi_skill&", true) and table.find(player.player_skills, function (s)
        return s.XushiSkill
      end)
    else
      return target == player and data.XushiSkill
    end
  end,
  on_refresh = function (self, event, target, player, data)
    local room = player.room
    if event == fk.AfterDrawPileShuffle then
      for _, s in ipairs(player.player_skills) do
        if player:getMark("@@lvshi_readying:::"..s.name) > 0 then
          room:setPlayerMark(player, "@@lvshi_readying:::"..s.name, 0)
          room.logic:trigger("fk.XushiSkillReady", player, s, false)
        end
      end
    elseif event == fk.EventAcquireSkill or event == fk.GamePrepared then
      room:handleAddLoseSkills(player, "lvshi_xushi_skill&", nil, false, true)
    elseif event == fk.EventLoseSkill then
      room:setPlayerMark(player, "@@lvshi_readying:::"..data.name, 0)
      if not table.find(player.player_skills, function (s)
        return s.XushiSkill
      end) then
        room:handleAddLoseSkills(player, "-lvshi_xushi_skill&", nil, false, true)
      end
    elseif event == fk.SkillEffect then
      room:setPlayerMark(player, "@@lvshi_readying:::"..data.name, 1)
      room.logic:trigger("fk.RfenghouSkillNulli", player, data, false)
    end
  end,
}
Fk:addSkill(xushi_skill)
Fk:addSkill(xushi_skill_trigger)

Fk:loadTranslationTable{
  ["lvshi_xushi_skill&"] = "蓄势技",
  [":lvshi_xushi_skill&"] = "出牌阶段，你可以弃置三张牌，重置一个本回合未发动过的蓄势技。",
  ["#lvshi_xushi_skill&"] = "你可以弃置三张牌，重置一个本回合未发动过的蓄势技",
  ["lvshi_xushi_skill"] = "一种技能标签，拥有此标签的技能发动后失效，直到牌堆洗切后重置；出牌阶段，你可以弃置三张牌，"..
  "重置一个本回合未发动过的蓄势技。",

  ["loseStrategy"] = "失去策略",
  ["#loseStrategy"] = "%from 失去了 %arg",
  ["#gainStrategy"] = "%from 获得了 %arg",
}

--[[以下搬运自封侯
  多项转换技：项数大于2的转换技
  用名为rfenghou_switch的角色标记储存所有这类转换技
  标记格式为{技能名，当前项数，总项数，标记颜色}
  请在多项转换技的on_aquire中使用lvshi.setSwitchState(player, skillName, 1, total)用于新建转换技
  lvshi.changeSwitchState(player, skillName, add)用于增加或减少当前项数
  若从最后一项返回第一项，触发fk.RfenghouSwitchLoopback（周转）事件
  注意，请务必在技能on_lose调用removeSwitchSkill清除标记占用

  标记命名格式：
  @!rfenghou_ .. 颜色 .. 项数(1~9)，如 @!rfenghou_red1
  大于等于10项时，标记命名为 @!rfenghou_redx ，使用标记数量代表当前项
  注意，UI上标记数至多显示为999
--]]


--- 更新角色转换技的UI标记。
--- @param player ServerPlayer @ 目标角色
--- @param color string @ 标记颜色
--- @param old? integer @ 旧项数
--- @param new? integer @ 新项数
local refreshSwitchMark = function (player, color, old, new)
  if old then
    if old < 10 then
      player.room:setPlayerMark(player, "@!rfenghou_"..color..old, 0)
    else
      player.room:setPlayerMark(player, "@!rfenghou_"..color.."x", 0)
    end
  end
  if new then
    if new < 10 then
      player.room:setPlayerMark(player, "@!rfenghou_"..color..new, 1)
    else
      player.room:setPlayerMark(player, "@!rfenghou_"..color.."x", new)
    end
  end
end

--- 将多项转换技的当前项数增加1项
---@param player ServerPlayer @ 目标角色
---@param skillName string @ 转换技总项数
---@param add? boolean @ 是否为增加，写false则后退一项。默认增加
lvshi.changeSwitchState = function (player, skillName, add)
  if player.dead then return end
  local room = player.room
  local mark = player:getTableMark("rfenghou_switch")
  local skillIndex
  for i, v in ipairs(mark) do
    if v[1] == skillName then
      skillIndex = i
      break
    end
  end
  if skillIndex == nil then
    p("Warning:Cannot find the index of the switch skill ["..skillName.."]. Check if its mark have been illegally removed or not added")
    return
  end
  local oldIndex, total, color = mark[skillIndex][2], mark[skillIndex][3], mark[skillIndex][4]
  local newIndex, loopBack
  if add or (add == nil) then
    newIndex = oldIndex + 1
    if newIndex > total then
      loopBack = true
      newIndex = 1
    end
  else
    newIndex = oldIndex - 1
    if newIndex < 1 then
      newIndex = total
    end
  end
  mark[skillIndex][2] = newIndex
  room:setPlayerMark(player, "rfenghou_switch", mark)
  if color then
    refreshSwitchMark(player, color, oldIndex, newIndex)
  end
  -- 触发“周转”
  if loopBack then
    room.logic:trigger("fk.RfenghouSwitchLoopback", player, {skillName = skillName}, false)
  end
end

--- 设置多项转换技的状态。也可用于新建多项转换技
---@param player ServerPlayer @ 目标角色
---@param skillName string @ 技能名
---@param index? integer @ 设置当前项数
---@param total? integer @ 转换技总项数
lvshi.setSwitchState = function (player, skillName, index, total)
  local mark = player:getTableMark("rfenghou_switch")
  local colors, oldIndex = {"red", "green", "yellow", "blue"}, nil
  local skillIndex -- 判断此转换技在该角色所有转换技的储存位置
  for i, v in ipairs(mark) do
    if v[1] == skillName then
      skillIndex = i
      break
    end
  end
  if skillIndex == nil then -- 找不到记录，则新建一个记录
    table.insert(mark, {skillName, index or 1, total or 3})
    skillIndex = #mark
    -- 为此转换技分配标记颜色
    local color = table.find(colors, function(c)
      return table.every(mark, function(v) return v[4] ~= c end)
    end)
    if color then
      mark[skillIndex][4] = color
    end
  else
    oldIndex = mark[skillIndex][2]
  end
  if index then
    mark[skillIndex][2] = index
  end
  if total then
    mark[skillIndex][3] = total
  end
  player.room:setPlayerMark(player, "rfenghou_switch", mark)
  if mark[skillIndex][4] then
    refreshSwitchMark(player, mark[skillIndex][4], oldIndex, index)
  end
end


--- 清除一个多项转换技
---@param player ServerPlayer @ 目标角色
---@param skillName string @ 技能名
lvshi.removeSwitchSkill = function (player, skillName)
  local mark = player:getTableMark("rfenghou_switch")
  local color, index
  for i, v in ipairs(mark) do
    if v[1] == skillName then
      if v[4] then
        color = v[4]
        index = v[2]
      end
      table.remove(mark, i)
      break
    end
  end
  player.room:setPlayerMark(player, "rfenghou_switch", mark)
  if color then
    refreshSwitchMark(player, color, index)
    -- 清除后可以把它分配的颜色转移给未分配颜色的技能，以后再说吧
  end
end


--- 获取多项转换技的当前项。若未记录，返回0
---@param player Player @ 目标角色
---@param skillName string @ 技能名
---@return integer
lvshi.getSwitchState = function (player, skillName)
  local mark = player:getTableMark("rfenghou_switch")
  for i, v in ipairs(mark) do
    if v[1] == skillName then
      return v[2]
    end
  end
  return 0
end

--警告！如果服务器没有江山如故/封侯/大剧杯扩展，则蓄谋无法正常使用
-- 将一张牌给某角色蓄谋
---@param player ServerPlayer @ 操作蓄谋的玩家
---@param card integer | Card  @ 用来蓄谋的牌
---@param skill_name? string @ 技能名
---@param proposer? integer @ 移动操作者的id
---@return nil
lvshi.premeditate = function(player, card, skill_name, proposer)
  skill_name = skill_name or ""
  proposer = proposer or player.id
  local room = player.room
  if type(card) == "table" then
    assert(not card:isVirtual() or #card.subcards == 1)
    card = card:getEffectiveId()
  end
  --local xumou = Fk:cloneCard("lvshi__premeditate")
  local xumou = Fk:cloneCard("tuntian__premeditate")
  xumou:addSubcard(card)
  room:sendLog{
    type = "#premeditate",
    from = proposer,
    to = {player.id},
  }
  player:addVirtualEquip(xumou)
  room:moveCardTo(xumou, Player.Judge, player, fk.ReasonJustMove, skill_name, nil, false, proposer, "", {proposer, player.id})
end

Fk:loadTranslationTable{
  ["#premeditate"] = "%from 进行了蓄谋",
}
--失败的视为装备牌
lvshi.FakeEquip = function(player, id, skill_name, proposer)
  skill_name = skill_name or ""
  proposer = proposer or player.id
  local room = player.room
  local choice = player.room:askForChoice(player, {"weapon", "armor", "offensive_ride", "defensive_ride", "treasure"}, "FakeEquip")
  local equip = Fk:cloneCard(skill_name.."_"..choice)
  equip:addSubcard(id)
  --local equip = room:printCard(skill_name.."_"..choice)
  room:sendLog{
    type = "#fake_equip",
    from = proposer,
    arg = Fk:getCardById(id):toLogString(),
  }
  player:addVirtualEquip(equip)
  room:moveCardTo(equip, Card.PlayerEquip, player, fk.ReasonJustMove, skill_name, nil, false, proposer, "", {proposer, player.id})
end

Fk:loadTranslationTable{
  ["FakeEquip"] = "视为装备牌",
  ["#fake_equip"] = "%from 将 %arg 置于装备区",
}

return lvshi
